﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//多态的条件
//1.虚函数重写
//2.父类的指针或者引用去调用虚函数

//虚函数重写的条件:
//继承关系父子的两个虚函数，三同(函数名/参数类型/返回值)
//三同(函数名/参数类型/返回值)的例外:
// 1.协变
//->返回值可以不同，但是必须是父子类关系的指针或引用
//virtual Person* BuyTicket()	virtual Student* BuyTicket()
// 2.析构函数的重写(基类与派生类析构函数的名字不同)

//派生类虚函数重写可以不加virtual(建议加上)

//virtual只能修饰成员函数，和虚继承没关系
class Person {
public:
	virtual void BuyTicket()
	{
		cout << "Person买票-全价" << endl;
	}
};
class Student : public Person {
public:
	virtual void BuyTicket()
	{
		cout << "Student买票-半价" << endl;
	}

//注意：在重写基类虚函数时，派生类的虚函数在不加virtual关键字时，
//虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类
//依旧保持虚函数属性),但是该种写法不是很规范，不建议这样使用
//void BuyTicket() { cout << "买票-半价" << endl; }
};
//ps子类继承父类的接口，重写函数的实现
//如果父类函数有缺省值，子类继承

void Func(Person& p)//形参是父类，能传父类和子类对象
{
	p.BuyTicket();//多态调用判断p是父类还是子类对象引用或指针，
}				//调对应函数
//void Func(Person p)
//{		//不是引用或指针，不能形成多态，Person类型对象
//	p.BuyTicket();//就调用Person内的函数
//}
void test1()
{
	Person ps;
	Student st;

	Func(ps);
	Func(st);
}

void test2()
{
	Person* p = new Person;
	delete p;//正确析构

	p = new Student;
	delete p;//非正确析构，会调用Person析构
	//应该让Person和Student形成多态
	//析构函数统一会被处理成destructor()
	//满足虚函数，满足指针调用
	//virtual ~Person(); virtual ~Studengt();
}						//子类virtual可以省

//ﬁnal：1.修饰虚函数，表示该虚函数不能再被重写
//		2.修饰类，不能被继承

//override: 检查派生类虚函数是否重写了基类某个虚函数，如果没有重写编译报错。
//virtual void Drive() override {cout << "Benz-舒适" << endl;}

//抽象类
//在虚函数的后面写上 =0 ，则这个函数为纯虚函数。
//包含纯虚函数的类叫做抽象类（也叫接口类），抽象类不能实例化出对象。
class Car//间接的强制派生类重写虚函数
{
public:
	//纯虚函数
	virtual void Drive() = 0;
};
class Benz :public Car 
{ public: //包含纯虚函数的派生类，必须重写虚函数
	virtual void Drive()	//才能实例化出对象
	{ 
		cout << "Benz-舒适" << endl; 
	} 
};
class BMW :public Car 
{ public:    
	virtual void Drive() 
	{ 
		cout << "BMW-操控" << endl; 
	} 
};
void test3() {
	//纯虚函数 派生类多态的使用
	Car* pBenz = new Benz;    
	pBenz->Drive();
	Benz bz;
	Car& car1 = bz;//切割
	car1.Drive();

	Car* pBMW = new BMW;    
	pBMW->Drive();
	BMW bw;
	Car& car2 = bw;
	car2.Drive();

}

//普通函数的继承是一种实现继承，
//虚函数的继承是一种接口继承


//虚函数表
// 这里常考一道笔试题：sizeof(Base)是多少？
class Base
{
public://除了_b成员，还多一个__vfptr放在对象的前面
	virtual void Func1()//对象中的这个指针我们叫做虚函数表指针
	{//把虚函数的地址传到了虚函数表中
		cout << "Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}

private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

void test4()
{
	//虚表是一个函数指针数组
	cout << sizeof(Base) << endl;
	//x86  8字节4+4		//没有虚函数就不存在虚表指针
	//x64  16字节4+8=12 内存对齐8，所以16
	cout << sizeof(Derive) << endl;
	//x86  12字节4+4+4	int+指针+指针
	//派生类也有虚表指针，虚表存重写的Derive::Func1的地址
	//拷贝一个新表，重写的Func1指针覆盖了父类的Func1指针

	//虚函数存在哪里？
	//虚函数和普通函数一样，存在代码段，同时把虚函数的地址
	//存了一份到虚函数表
	
	//虚函数表存在哪里？
	//在编译阶段生成，存在代码段(常量区)
	//虚函数表指针，在构造时才初始化给对象
}


//多态的原理
class Person2 {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }

};
class Student2 : public Person2 {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }

};

void Func(Person2& p)
{
	p.BuyTicket();
}
void test5()
{
	Person2 Mike;
	Func(Mike);
	//收到父类对象，给父类引用，函数内调用
	//虚函数，通过存在父类里的虚表指针找到虚表
	//虚表中存着函数地址，成功调用函数

	Student2 Johnson;
	Func(Johnson);
	//收到子类对象，切片给父类引用，函数内调用
	//虚函数，通过存在子类里的虚表指针找到更新后虚表
	//虚表中存着更新后函数地址，成功调用函数

	//注意，表1和表2不是一个表，表2是表1的拷贝后覆盖

	//子类对象赋值给父类不会拷贝虚函数表指针
	//只拷贝子类对象成员

	//同一个类只有一张虚表，不同对象共用一张虚表
	//父类和子类即使没有重写，也不是一张虚表
}


//虚函数的地址一定会被放进类的虚函数表吗？
class Base2 {//一定会
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int a;
};
class Derive2 :public Base2 {
public:
	virtual void func1() { cout << "Derive2::func1" << endl; }
	virtual void func3() { cout << "Derive2::func3" << endl; }
	virtual void func4() { cout << "Derive2::func4" << endl; }
private://监视窗口显示虚表里没有func3,func4,是假的
	int b;//Derive2虚表有四个虚函数地址，两个继承，两个新增
};			//重写fun1
class X :public Derive2 {
public:
	virtual void func3() { cout << "X::func3" << endl; }
private://X虚表有四个虚函数地址，四个继承
		//重写fun3
	int b;
};
void test6()
{					//ps一个类有一个虚表
	Base2 b1;
	Derive2 d1;
	X x1;

	Derive2* p = &d1;
	p->func3();
	p = &x1;
	p->func3();//可以正常重载，证明虚表里有对应虚函数地址
}

//多继承中的虚函数表
class Base3 {
public:
	virtual void func1() { cout << "Base3::func1" << endl; }
	virtual void func2() { cout << "Base3::func2" << endl; }
private:
	int b1;
};
class Base4 {
public:
	virtual void func1() { cout << "Base4::func1" << endl; }
	virtual void func2() { cout << "Base4::func2" << endl; }
private:int b2;
};
class Derive3 : public Base3, public Base4 {
public:
	virtual void func1() { cout << "Derive3::func1" << endl; }
	virtual void func3() { cout << "Derive3::func3" << endl; }
private://两个虚表分别继承了Base3和Base4
	int d1;//func1都被重写，func3存在继承Base3的虚表
			//即新虚函数放在第一个虚表
	//表1中func1的地址和表2中func1的地址不一样
	//但是指向同一个func1函数地址
	//以下不用掌握
	//其实虚表中存的不是真实的函数地址
	//指向一系列汇编操作，找到函数地址
	//p2调用func1,this指针指向不对，指针偏移在汇编中修正
	//p1不需要，所以虚表中func1地址不一样，因为指向不同操作
};
void test7()
{
	Derive3 d1;
	Base3* p1 = &d1;
	p1->func1();

	Base4* p2 = &d1;
	p2->func1();
}

//inline函数可以是虚函数吗？答：可以，普通调用正常内联展开，
// 多态调用编译器就忽略inline属性，这个函数就不再是
//inline，因为虚函数要放到虚表中去。


int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	//test6();
	test7();
}